#!/user/bin/env python
# -*- coding: utf-8 -*-
"""
Author: Sjy
LastEditTime: 2024-08-28 17:50:50
Description: 封装葛工提供的接口和类， 对其封装的目的是便于检查和维护
"""

__all__ = []

import traceback
import types
import os

from PyQt5 import QtCore, QtGui, QtWidgets

import gui.controls.globalvar as GL
import gui.controls.settings as ST
import gui.components.util_file as UFile
import gui.controls.signals as mySigs

signals = mySigs.signals


def _is_development():
    data = ST.get_index_data() or {}
    return data.get("environment", "production") == "development"


def _get_py3d():
    return GL.get_value("py3d")


def _run_api(callback, *args, **kwargs):
    """运行接口"""
    if _is_development():
        """开发环境下是需要将错误完全暴漏出来的"""
        status = callback(*args, **kwargs)
        return status if status != None else True
    else:
        """生产环境下尽量不要让程序挂掉,所以要加上try,保证程序正常运行,同时将错误打印出来"""
        try:
            status = callback(*args, **kwargs)
            return status if status != None else True
        except:
            traceback.print_exc()
        return False


def _set_attr(ins, attr, value, fset=False):
    """设置对象属性接口
    ins: 对象
    attr: 属性名
    value: 属性值
    fset: 强制赋值
    """
    if not fset and getattr(ins, attr, "undefined_attribute") == "undefined_attribute":
        """不强制赋值，则需要先判断属性是否存在,属性不存在则直接退出"""
        return
    setattr(ins, attr, value)

    # return print("文件打开失败，请检查!")


# 模型显示 ==================================================================================================
def _open_model(
    onOpenModel, file, *args, done=None, emits=[], fileType="vtk", **kwargs
):
    """打开模型文件
    onOpenModel: 打开模型文件类型的函数，不同类型的模型文件打开函数不同
    file: 文件路径
    done: 打开成功后的立即执行的回调函数，注意此函数必定在信号触发之前执行
    emits: 如果打开成功需触发的相关信号
    """
    file = file or UFile.open_model(fileType)
    status = None

    if file:
        status = _run_api(onOpenModel, file, **kwargs)
        if status:
            if isinstance(done, types.FunctionType):
                done(file)
            for emit in emits or []:
                getattr(signals, emit).emit(file)
    show_grid()
    return status


def open_vtk(
    file: str = None, emits=["sigOpenVtk", "sigOpenModel"], append=None, done=None
):
    """打开vtk文件
    file: 文件路径
    append: 是否追加
    emits: 如果打开成功需触发的相关信号
    cbf: 打开成功后的回调函数，注意此回调函数必定在信号触发之前执行
    """
    return _open_model(
        _get_py3d().openVtk, file, append=append, emits=emits, done=done, fileType="vtk"
    )


def open_vtu(file: str = None, emits=["sigOpenVtu", "sigOpenModel"], done=None):
    """打开vtk文件
    file: 文件路径
    append: 是否追加
    emits: 如果打开成功需触发的相关信号
    cbf: 打开成功后的回调函数，注意此回调函数必定在信号触发之前执行
    """
    return _open_model(
        _get_py3d().openVtu, file, emits=emits, done=done, fileType="vtu"
    )


def open_inp(file: str = None, emits=["sigOpenInp", "sigOpenModel"], done=None):
    """打开vtk文件
    file: 文件路径
    append: 是否追加
    emits: 如果打开成功需触发的相关信号
    cbf: 打开成功后的回调函数，注意此回调函数必定在信号触发之前执行
    """
    return _open_model(
        _get_py3d().openInpFile, file, emits=emits, done=done, fileType="inp"
    )


def open_vtk_group(dir: str = None, emits=[], done=None):
    """打开vtk组文件"""
    return _open_model(_get_py3d().openVtkGroup, dir, emits=emits, done=done)


def open_vtu_group(dir: str = None, emits=[], done=None):
    """打开vtk组文件"""
    return _open_model(_get_py3d().openVtuGroup, dir, emits=emits, done=done)


def show_grid(showGridModel: bool = None):
    """设置是否隐藏线"""
    if showGridModel == None:
        showGridModel = GL.get_value("showGridModel", True)
    if showGridModel:
        Theme().setThemEnableLine(True)
        _run_api(_get_py3d().setLineOn)
        _run_api(_get_py3d().setVertexOn)
    else:
        Theme().setThemEnableLine(False)
        _run_api(_get_py3d().setVertexOff)
        _run_api(_get_py3d().setLineOff)


# 视图 ==================================================================================================
def set_view_bgcolor(colors):
    """设置视图背景色"""
    return _run_api(_get_py3d().setViewportBackground, colors)


def hide_view_tab(flag: bool = True):
    """隐藏tabbar"""
    _set_attr(_get_py3d(), "hideTabBar", flag)


def add_view_tab(widget: QtWidgets.QWidget = None):
    """widget为pyqt的widget对象，这样可以嵌入到中心的tab里面
    Args:
        widget: QWidget对象
    Returns:
        None
    """
    if not widget:
        return
    return _run_api(_get_py3d().addTabWidget, widget)


# 获取当前的index
def get_cur_view_index() -> int:
    """获取当前的tab页索引
    Args:
        无
    Returns:
        int: QWidget的tab的索引
    """
    return _run_api(_get_py3d().get_cur_tab_index)


# 关闭 view page 通过当前index 关闭
def close_view_by_index(index: int = None) -> None:
    """关闭指定索引的tab页
    Args:
        index: QWidget的tab的索引
    Returns:
        None
    """
    return _run_api(_get_py3d().close_tab3d, index)


def close_current_view():
    """关闭当前的view page
    Args:
        无
    Returns:
        None
    """
    index = get_cur_view_index()
    if index != None:
        close_view_by_index(index)


# 命令 ==================================================================================================
def pexecute(cmd: str, shell=False, isEmit=False, cwd=None, clearPythonEnv=False):
    """执行命令"""
    env = os.environ.copy()
    if clearPythonEnv:
        if "PYTHONHOME" in env:
            del env["PYTHONHOME"]
        if "PYTHONPATH" in env:
            del env["PYTHONPATH"]
    return _run_api(
        _get_py3d().pexecute, cmd, shell=shell, isEmit=isEmit, cwd=cwd, env=env
    )


def pexecute_emit(cbf: types.FunctionType = None) -> None:
    """返回消息
    注意：要传入lambda 函数 不能是类的方法
    """
    if cbf and isinstance(cbf, type(lambda x: x)):
        _get_py3d().backMsg.connect(lambda msg, cbf=cbf: cbf(msg))


def pexecute_exit(cbf: types.FunctionType = None) -> None:
    """返回消息"""
    if cbf and isinstance(cbf, type(lambda x: x)):
        _get_py3d().subExit.connect(
            lambda returnCode, cmd, cbf=cbf: cbf(returnCode, cmd)
        )


# 后处理-阈值 ==================================================================================================
def get_cell_names():
    """获取单元名称"""
    return _run_api(_get_py3d().getCellArrayNames)


def get_point_names():
    """获取单元名称"""
    return _run_api(_get_py3d().getPointArrayNames)


def get_color_range(text, flag):
    """获取颜色范围"""
    return _run_api(_get_py3d().getColorRange, text, flag)


def show_color_range(text: str = "", flag: str = "", colorRange: list = []):
    """设置是否显示颜色范围"""
    colorRange = colorRange or get_color_range(text, flag)
    print("colorRange", colorRange)
    return _run_api(_get_py3d().setShowColorRange, text, flag, colorRange)


def enable_contour(values: list = [], input_type: str = "", input_name: str = ""):
    """开启等值面显示"""
    return _run_api(_get_py3d().enableContour, values, input_type, input_name)


def disable_contour():
    """关闭等值面显示  contour:等高线"""
    return _run_api(_get_py3d().disableContour)


def get_show_threshold_range(name, flag):
    """获取显示的场阈值范围"""
    return _run_api(_get_py3d().getShowColorRange, name, flag)


# 主题 ==================================================================================================
class Theme:
    # 获取主题属性信息
    def getTheme(self, *args, toShow=True, **kwargs):
        try:
            data = _get_py3d().getTheme()
            if toShow:
                return self.theme_to_show(data)
            return data
        except:
            pass

    # 设置主题属性信息
    def setTheme(self, themeData: dict = {}, *args, **kwargs):
        try:
            theme = self.theme_to_save(themeData)
            if theme:
                _get_py3d().setTheme(theme)
            else:
                print("未知错误！！！主题属性设置失败")
        except:
            pass

    def getPointTheme(self, *args, **kwargs):
        data = self.getTheme() or {}
        showPointTheme = {}
        if "nodeSize" in data:
            showPointTheme["nodeSize"] = data.get("nodeSize")
        if "nodeColor" in data:
            showPointTheme["nodeColor"] = data.get("nodeColor")
        return showPointTheme

    # 设置点主题
    def setPointTheme(self, data: dict = {}):
        # display = data.get('pointDisplay')
        # if display: # 显示点
        #     self.setVertexOn()
        # else:
        #     self.setVertexOff()
        saveThemeData = {}
        if "nodeColor" in data:
            saveThemeData["nodeColor"] = data.get("nodeColor")
        if "nodeSize" in data:
            saveThemeData["nodeSize"] = data.get("nodeSize")
        self.setTheme(saveThemeData)

    def theme_to_save(self, data: dict = {}):
        theme = self.getTheme(toShow=False)
        if not theme:
            return
        # 点大小
        if "nodeSize" in data:
            theme.nodeSize = data.get("nodeSize")

        if "nodeColor" in data:
            theme.nodeColor = data.get("nodeColor")
            # 设置 actorColor 的目的是 点可能是actor的一部分
            theme.actorColor = data.get("nodeColor")

        # 线颜色  注意颜色可能一般为 hsv 格式
        if "lineColor" in data:
            theme.lineColor = data.get("lineColor")
        # 面颜色
        if "faceColor" in data:
            theme.faceColor = data.get("faceColor")

        return theme

    def theme_to_show(self, theme: dict = {}):
        showData = {}
        # 点
        showData["nodeSize"] = theme.nodeSize
        showData["nodeColor"] = theme.nodeColor or theme.actorColor
        # 线
        return showData

    # 设置显示点颜色开启
    def setVertexOn(self):
        try:
            _get_py3d().setVertexOn()
        except:
            pass

    # 设置显示点颜色关闭
    def setVertexOff(self):
        try:
            _get_py3d().setVertexOff()
        except:
            pass

    def setThemEnableLine(self, status: bool = True):
        theme = self.getTheme(toShow=False) or {}
        theme.enableLine = status
        _get_py3d().setTheme(theme)


if __name__ == "__main__":
    # 接口使用示例
    open_vtk("data/vtk/test.vtk", emits=[])
    hide_view_tab(True)
    set_view_bgcolor([[93, 212, 255], [3, 22, 52]])
